/*
    Author: Christopher Diggins
    License: MIT Licence 1.0
    Website: http://www.heron-language.com
*/

#ifndef XML_PARSER_HPP
#define XML_PARSER_HPP

namespace xml
{
	using namespace yard;
	using namespace std;

	class XmlParser 
	{
	public:
	
		typedef SimpleTextParser Parser;
		typedef Parser::Iterator Iterator;
		typedef Parser::Token Token; 
		typedef Parser::Tree Tree;
		typedef Parser::Node Node;

		XmlParser() 
				: parser(NULL), filename(""), data(NULL), size(-1)
		{
		}

		~XmlParser() {
			Clear();
		}

		void Clear() {
			if (parser) delete parser; parser = NULL;
			if (data) free((void*)data); data = NULL;
			filename = "";
			size = -1;
		}

		void LoadFile(const char* name) {
			Clear();
			filename = name;
			size = FileSize(filename);
			if (size == 0) {
				printf("could not open file %s\n", filename);
				return;
			}
			//printf("File size = %d\n", datasize);
			data = ReadFile(filename, size);
				if (data == NULL) {
				printf("Could not load file %s\n", filename);
				return;
			}
		}

		void Parse() {
			parser = new yard::SimpleTextParser(data, data + size);	   
			if (!parser->Parse<xml_grammar::Document>()) {
				printf("Failed to parse file %s\n", filename);
				parser->OutputLocation();
				return;
			}
			try {
				document.Create(parser->GetAstRoot());
			}
			catch (...) {
				printf("Error occured during AST construction\n");
				throw;
			}
		}

	// Member classes 
	public:	

		struct XmlNode 
		{
			Iterator first;
			Iterator last; 
			vector<XmlNode*> children;
			
			XmlNode(Node* node) 
				: first(node->Begin()), last(node->End())
			{
				for (Node* p = node->GetFirstChild(); p != NULL; p = p->GetSibling()) {
					AddChild(p);
				}
			}

			virtual ~XmlNode() 
			{
				Clear();
			}
			
			void Clear() {
				for (size_t i=0; i < children.size(); ++i) 
					delete children[i];
				children.clear();
			}

			void AddChild(Node* node) {
				children.push_back(CreateXmlNode(node));
			}

			int GetNumChildren() {
				return children.size();
			}

			template<typename T>
			T* GetChild(int n) {
				return dynamic_cast<T*>(children[n]);
			}

			XmlNode* CreateXmlNode(Node* node) {
				XmlNode* r = NULL;
				if (node->TypeMatches<xml_grammar::Element>()) {
					if (node->GetNumChildren() == 1) {
						r = new XmlSimpleElement(node);
					}
					else if (node->GetNumChildren() == 3) {
						r = new XmlCompoundElement(node);
					}
					else {
						Error("invalid Element node, expected either 1 or 3 children");
					}
				}
				else if (node->TypeMatches<xml_grammar::STag>()) {
					r = new XmlTag(node);
				}
				else if (node->TypeMatches<xml_grammar::ETag>()) {
					r = new XmlTag(node);
				}
				else if (node->TypeMatches<xml_grammar::Name>()) {
					r = new XmlNode(node);
				}
				else if (node->TypeMatches<xml_grammar::Content>()) {
					r = new XmlContent(node);
				}
				else if (node->TypeMatches<xml_grammar::AttValue>()) {
					r = new XmlAttValue(node);				
				}
				else {
					Assert("unrecognized YARD tree node");
				}
			}

			Iterator Begin() {
				return first;
			}

			Iterator End() { 
				return last;
			}

			string ToStr() {
				return string(Begin(), End());
			}
		};

		struct XmlName : XmlNode
		{
			XmlName(Node* node)
				: XmlNode(node)
			{ 
				Assert(GetNumChildren() == 1);
			}
		};

		struct XmlAttValue : XmlNode
		{
			XmlAttValue(Node* node)
				: XmlNode(node)
			{ 
				Assert(GetNumChildren() == 1);
			}
		};

		struct XmlAtt : XmlNode
		{
			XmlAtt(Node* node) 
				: XmlNode(node)
			{
				Assert(GetNumChildren() == 2);
			}

			string GetName() {
				return GetChild<XmlName>(0)->ToStr();
			}

			string GetRawValue() {
				return GetChild<XmlAttValue>(1)->ToStr();
			}

			string GetValue() {
				string r = GetRawValue();
				if (r.size() > 1) {
					if (r[0] == '\'' || r[0] == '\"') {
						return r.substr(0, r.size() - 2);
					}
				}
				return r;
			}
		};

		struct XmlAttList : XmlNode
		{
			XmlAttList(Node* node) 
				: XmlNode(node)
			{ 
			}
		};

		struct XmlTag : XmlNode
		{
			XmlTag(Node* node) 
				: XmlNode(node)
			{ 
				Assert(GetNumChildren() == 2);
			}

			string GetName() {
				return GetChild<XmlName>(0)->ToStr();
			}

			XmlAttList* GetAttList() {
				return GetChild<XmlAttList>(1);
			}
		};

		struct XmlElement : XmlNode
		{
			XmlElement(Node* node)
				: XmlNode(node)
			{ }

			virtual XmlAttList* GetAttList() = 0;
			virtual string GetName() = 0;
			virtual int GetNumSubElements() = 0;
			virtual XmlElement* GetSubElement(int n) = 0;
		};

		struct XmlContent : XmlNode
		{
			XmlContent(Node* node)
				: XmlNode(node)
			{
				for (int i=0; i < GetNumChildren(); ++i) {
					Assert(GetSubElement(i) != NULL);
				}
			}

			int GetNumSubElements() {
				return GetNumChildren();
			}

			XmlElement* GetSubElement(int n) {
				return GetChild<XmlElement>(n);
			}
		};

		struct XmlSimpleElement : XmlElement
		{
			XmlSimpleElement(Node* node) 
				: XmlElement(node)
			{
				Assert(node->GetNumChildren() == 1);
			}

			XmlTag* GetTag() {
				return GetChild<XmlTag>(0);
			}

			XmlAttList* GetAttList() {
				return GetTag()->GetAttList();
			}

			string GetName() {
				return GetTag()->GetName();
			}

			int GetNumSubElements() {
				return 0;
			}

			XmlElement* GetSubElement(int n) {
				Assert(false);
				return NULL;
			}
		};	

		struct XmlCompoundElement : XmlElement
		{
			XmlCompoundElement(Node* node) 
				: XmlElement(node)
			{
				Assert(node->GetNumChildren() == 3);
				Assert(GetStartTag()->GetName() == GetEndTag()->GetName());
			}

			XmlTag* GetStartTag() {
				return GetChild<XmlTag>(0);
			}

			string GetName() {
				return GetStartTag()->GetName();
			}

			XmlAttList* GetAttList() {
				return GetStartTag()->GetAttList();
			}

			XmlContent* GetContent() {
				return GetChild<XmlContent>(1);
			}

			XmlTag* GetEndTag() {
				return GetChild<XmlTag>(2);
			}

			int GetNumSubElements() {
				return GetContent()->GetNumSubElements();
			}

			XmlElement* GetSubElement(int n) {
				return GetContent()->GetSubElement(n);
			}

		};

		class XmlDocument
		{
			XmlNode* root;
		
		public:
			XmlDocument()
				: root(NULL)
			{ }

			~XmlDocument()
			{
				if (root) delete root; root = NULL;
			}

			void Create(Node* node) {
			}
		};

	private: 

		Parser* parser;
		const char* filename;
		const char* data;
		int size;
		XmlDocument document;
	};
}

#endif 
